// arch-tag: ecd8a6f9-8b09-4280-8e4a-7bae096bcbde
package de.yvert.jingle.hdr;

import de.yvert.jingle.Image;
import de.yvert.jingle.ImageType;

/**
 * Contains color samples for a cubical area - simply put: an image.
 * Uses an array of float for its internal storage.
 * 
 * This class provides a number of data manipulation functions.
 * 
 * @author Ulf Ochsenfahrt
 */
public class HdrImage3D implements Image
{

protected float[] data;
protected int width, height, depth;

public HdrImage3D(int width, int height, int depth, float[] data)
{
	if ((width <= 0) || (height <= 0) || (depth <= 0))
		throw new IllegalArgumentException("An image cannot have negative dimensions.");
	if (data.length != 4*width*height*depth)
		throw new IllegalArgumentException("data doesn't have correct length!");
	this.width = width;
	this.height = height;
	this.depth = depth;
	this.data = data;
}

public HdrImage3D(int width, int height, int depth)
{ this(width, height, depth, new float[width*height*depth*4]); }

public HdrImage3D(HdrImage3D other)
{
	this(other.width, other.height, other.depth);
	System.arraycopy(other.data, 0, this.data, 0, other.data.length);
}

public ImageType getType()
{ return ImageType.RGBA_3D_FLOAT; }

public int getWidth()
{ return width;  }

public int getHeight()
{ return height; }

public int getDepth()
{ return depth; }

public float[] getData()
{ return data; }

public int getPixelAsInt(int x, int y, int z)
{
	int offset = (z*width*height+y*width+x)*4;
	
	double r = Math.round(data[offset+0]*255);
	double g = Math.round(data[offset+1]*255);
	double b = Math.round(data[offset+2]*255);
	double a = Math.round(data[offset+3]*255);
	if (r < 0) r = 0; if (r > 255) r = 255;
	if (g < 0) g = 0; if (g > 255) g = 255;
	if (b < 0) b = 0; if (b > 255) b = 255;
	if (a < 0) a = 0; if (a > 255) a = 255;
	
//	return (((int) a) << 24) | (((int) r) << 16) | (((int) g) << 8) | ((int) b);
	return (((int) r) << 16) | (((int) g) << 8) | ((int) b);
}

public float getRed(int x, int y, int z)
{
	int offset = (z*width*height+y*width+x)*4+0;
	return data[offset];
}

/*public void getPixel(int x, int y, int z, Color color)
{
	int offset = (z*width*height+y*width+x)*4;
	System.arraycopy(data, offset, color.rgb, 0, 3);
}

public void setPixel(int x, int y, int z, Color color)
{
	int offset = (z*width*height+y*width+x)*4;
	System.arraycopy(color.rgb, 0, data, offset, 3);
}*/

public void setPixel(int x, int y, int z, float r, float g, float b)
{
	int offset = (z*width*height+y*width+x)*4;
	data[offset+0] = r;
	data[offset+1] = g;
	data[offset+2] = b;
}

public void setPixel(int x, int y, int z, float[] rgb)
{
	int offset = (z*width*height+y*width+x)*4;
	data[offset+0] = rgb[0];
	data[offset+1] = rgb[1];
	data[offset+2] = rgb[2];
}

public void setPixel(int addr, float gray)
{
	int offset = addr*4;
	data[offset+0] = gray;
	data[offset+1] = gray;
	data[offset+2] = gray;
}

public void setPixelRGB(int x, int y, int z, int rgb)
{
	int offset = (z*width*height+y*width+x)*4;
	data[offset+0] = ( rgb        & 0xff)/255.0f;
	data[offset+1] = ((rgb >>  8) & 0xff)/255.0f;
	data[offset+2] = ((rgb >> 16) & 0xff)/255.0f;
}

public void setPixelBGR(int x, int y, int z, int rgb)
{
	int offset = (z*width*height+y*width+x)*4;
	data[offset+2] = ( rgb        & 0xff)/255.0f;
	data[offset+1] = ((rgb >>  8) & 0xff)/255.0f;
	data[offset+0] = ((rgb >> 16) & 0xff)/255.0f;
}

public void setPixelBGRA(int x, int y, int z, int rgba)
{
	int offset = (z*width*height+y*width+x)*4;
	data[offset+2] = ( rgba        & 0xff)/255.0f;
	data[offset+1] = ((rgba >>  8) & 0xff)/255.0f;
	data[offset+0] = ((rgba >> 16) & 0xff)/255.0f;
	data[offset+3] = 1;
}

/*public void addPixel(int x, int y, int z, Color col)
{
	int offset = (z*width*height+y*width+x)*4;
	data[offset+0] += col.getR();
	data[offset+1] += col.getG();
	data[offset+2] += col.getB();
}*/

public void addPixel(int x, int y, int z, float r, float g, float b)
{
	int offset = (z*width*height+y*width+x)*4;
	data[offset+0] += r;
	data[offset+1] += g;
	data[offset+2] += b;
}

public void add(HdrImage3D other)
{
	for (int i = 0; i < data.length; i++)
		data[i] += other.data[i];
}

public void divide(float divisor)
{
	for (int i = 0; i < data.length; i++)
		data[i] /= divisor;
}

public void decodeRGB(byte[] arr)
{
	for (int i = 0; i < arr.length/3; i++)
	{
		data[4*i+0] = (arr[3*i+0] & 0xff)/255.0f;
		data[4*i+1] = (arr[3*i+1] & 0xff)/255.0f;
		data[4*i+2] = (arr[3*i+2] & 0xff)/255.0f;
		data[4*i+3] = 1;
	}
}

public void decodeBGR(byte[] arr)
{
	for (int i = 0; i < arr.length/3; i++)
	{
		data[4*i+2] = (arr[3*i+0] & 0xff)/255.0f;
		data[4*i+1] = (arr[3*i+1] & 0xff)/255.0f;
		data[4*i+0] = (arr[3*i+2] & 0xff)/255.0f;
		data[4*i+3] = 1;
	}
}

public void decodeBGRA(byte[] arr)
{
	for (int i = 0; i < arr.length/4; i++)
	{
		data[4*i+2] = (arr[4*i+0] & 0xff)/255.0f;
		data[4*i+1] = (arr[4*i+1] & 0xff)/255.0f;
		data[4*i+0] = (arr[4*i+2] & 0xff)/255.0f;
		data[4*i+3] = (arr[4*i+3] & 0xff)/255.0f;
	}
}

public void decodeRGB(int[] arr)
{
	for (int i = 0; i < arr.length; i++)
	{
		int color = arr[i];
		data[4*i+0] = ((color >>  0) & 0xff)/255.0f;
		data[4*i+1] = ((color >>  8) & 0xff)/255.0f;
		data[4*i+2] = ((color >> 16) & 0xff)/255.0f;
		data[4*i+3] = 1;
	}
}

private int map(float gray)
{
	int result = Math.round(gray*255.0f);
	if (result < 0) result = 0;
	if (result > 255) result = 255;
	return result;
}

public void encodeRGB(int[] arr)
{
	for (int i = 0; i < arr.length; i++)
	{
		int color = 0;
		color |= map(data[4*i+0]) << 0;
		color |= map(data[4*i+1]) << 8;
		color |= map(data[4*i+2]) << 16;
		arr[i] = color;
	}
}

/*public void flipY()
{
	float[] temp = new float[4*width];
	for (int y = 0; y < height/2; y++)
	{
		System.arraycopy(data, 4*width*y, temp, 0, 4*width);
		System.arraycopy(data, 4*width*(height-1-y), data, 4*width*y, 4*width);
		System.arraycopy(temp, 0, data, 4*width*(height-1-y), 4*width);
	}
}*/

}
